//
// Created by vivi on 15/04/2018.
//

#pragma once

#include <core/constant/constants.h>
#include "collisionobj.h"
#include "../collider/sdfcollider.h"
#include "../constant/constants.h"
#include "VPly/vply.h"

namespace HairEngine {
	class SignedDistanceFieldCollisionObject: public CollisionObject {
	Public:
		SignedDistanceFieldCollisionObject(const std::string &filePath, const Eigen::Affine3f & transform):
				CollisionObject(transform),
				collider(filePath) {}

		SignedDistanceFieldCollisionObject(const std::string &filePath,
		                                   const Eigen::Affine3f & transform,
		                                   const Eigen::Affine3f & previousTransform,
		                                   const float t):
				CollisionObject(transform, previousTransform, t),
				collider(filePath) {}

		Eigen::AlignedBox3f boundingBox() const override {
			return Eigen::AlignedBox3f(collider.minP, collider.maxP);
		}

		float modelDistance(const Eigen::Vector3f & modelPos, Eigen::Vector3f *outGradientPtr = nullptr) const override {
			return collider.distance(modelPos, outGradientPtr);
		}

		std::ostream & vplyStream(std::ostream & os) const override {

			if (!vplyShouldWriteCell && !vplyShouldWriteNormal)
				return os;

			for (size_t dx = 0; dx < collider.nx - 1; ++dx)
				for (size_t dy = 0; dy < collider.ny - 1; ++dy)
					for (size_t dz = 0; dz < collider.nz - 1; ++dz) {
						Eigen::Vector3f center = {
								collider.minP.x() + (dx + 0.5f) * collider.cellSize.x(),
								collider.minP.y() + (dy + 0.5f) * collider.cellSize.y(),
								collider.minP.z() + (dz + 0.5f) * collider.cellSize.z()
						};

						const auto cell = collider.getCell(center, nullptr);
						for (size_t i = 1; i < 7; ++i)
							if ((cell.v[i]->distance >= 0 && cell.v[0]->distance < 0)
							    || (cell.v[i]->distance <= 0 && cell.v[0]->distance > 0)) {

								if (vplyShouldWriteCell)
									VPly::writePoint(
											os,
											EigenUtility::toVPlyVector3f(center),
											VPly::VPlyIntAttr("type", VPlyOptions::CollisionObject_)
									);

								if (vplyShouldWriteNormal) {
									Eigen::Vector3f gradient;
									distance(center, &gradient);

									VPly::writeLine(
											os,
											EigenUtility::toVPlyVector3f(center),
											EigenUtility::toVPlyVector3f(center + 0.05f * gradient.normalized()),
											VPly::VPlyIntAttr("type", VPlyOptions::CollisionObject_)
									);
								}

								break;
							}
					}

			return os;
		}

	//vplyStream options
	bool vplyShouldWriteCell = true;
	bool vplyShouldWriteNormal = false;

	Protected:
		SDFCollider collider;
	};
}
